package model;

import java.util.ArrayList;
import model.ds.*;
import common.Tuple;

public class SwarmModel extends AbstractSwarmModel implements SwarmInterface{
	GameBoard board;
	
	
	/**
	 * Introduces the first piece (which is of the white player, who always
	 * starts the game) to the game.
	 * @param pieceType the type of the first piece
	 */
	public SwarmModel(){
		board=new GameBoard();
	
	}
	
	/**
	 * Returns the player whose turn it is.
	 * @return the player whose turn it is
	 */
	@Override
	public PlayerColor getCurrentPlayer() {
		return board.getCurrentPlayer();
	}

	/**
	 * <p>Returns a list of fields on which the current player can introduce new
	 * pieces.</p>
	 * <p>The list contains {@link Tuple}s of an integer value and a
	 * {@link HexDirection}. Each tuple denotes a field that is adjacent to a
	 * field that already contains a piece. The integer number denotes the
	 * piece (in the range {@code [0,#introduced-1]}). The second value is the
	 * direction in which the field lies.</p>
	 * @return a list of possible fields for introduction
	 */
	@Override
	public ArrayList<Tuple<Integer, HexDirection>> getIntroduceable() {
		return board.getIntroduceable();
	}

	/**
	 * Returns the total number of pieces already introduced.
	 * @return the total number of pieces already introduced
	 */
	@Override
	public int getIntroducedPieces() {
		return board.getIntroduced();
	}

	/**
	 * Returns the type of piece (defined by its id).
	 * @param pieceIndex the index of the piece
	 * @return the type of the piece
	 */
	@Override
	public PieceType getPieceType(int pieceIndex) {
		return board.getPieceType(pieceIndex);
	}

	/**
	 * <p>Returns a list of fields which are reachable by a piece.</p>
	 * <p>The list contains {@link Tuple}s of an integer value and a
	 * {@link HexDirection}. Each tuple denotes a field that is adjacent to a
	 * field that already contains a piece. The integer number denotes the
	 * piece (in the range {@code [0,#introduced-1]}). The second value is the
	 * direction in which the field lies.</p>
	 * @param pieceIndex the index of the piece
	 * @return a list of fields reachable by the piece
	 */
	@Override
	public ArrayList<Tuple<Integer, HexDirection>> getReachable(int pieceIndex) {
		if(pieceIndex>=board.getIntroduced()) throw new IllegalArgumentException("This Piece " + pieceIndex + " isnt set yet.");
		return board.pieces[pieceIndex].getReachableFields(board.positions[pieceIndex]);
	}

	/**
	 * Returns the number of remaining pieces of a given type for a given player.
	 * @param player the player
	 * @param type the piece type
	 * @return returns the number of remaining pieces for the player
	 */
	@Override
	public int getRemainingPieces(PlayerColor player, PieceType type) {
		return board.getLeftPiece(player, type);
	}

	/**
	 * Introduces the first piece (which is of the white player, who always
	 * starts the game) to the game.
	 * @param pieceType the type of the first piece
	 */
	@Override
	public void introduce(PieceType pieceType) {
		try {
			board.introduce(pieceType);
			IntroductionNotification note = new IntroductionNotification(0, null, 0, getCurrentPlayer());
			board.setCurrentPlayer(getCurrentPlayer().toggle());
			notifyObservers(note);
			
		} catch (IllegalStateException e) {
			System.out.println(e.getMessage());
		} catch (IllegalArgumentException e) {
			System.out.println(e.getMessage());
		}
	}

	/**
	 * Introduces the first piece for the black player (which has to be adjacent
	 * to the first (white) piece).
	 * @param pieceType the type of the first black piece
	 * @param hexDirection the relative position of the piece to the first white piece
	 */
	@Override
	public void introduce(PieceType pieceType, HexDirection hexDirection) {
		try {
			board.introduce(pieceType, hexDirection);
			IntroductionNotification note = new IntroductionNotification(1, hexDirection, 0, getCurrentPlayer());
			board.setCurrentPlayer(getCurrentPlayer().toggle());
			notifyObservers(note);
		} catch (IllegalStateException e) {
			System.out.println(e.getMessage());
		} catch (IllegalArgumentException e) {
			System.out.println(e.getMessage());
		}

	}

	/**
	 * <p>Introduces a piece to the game (for the current player). The position is
	 * specified relatively to some other piece already introduced to the game.</p>
	 * <p>The anchor piece is specified by an index which is in the range of
	 * {@code [0,#introduced-1]}. The direction is then specified relatively to
	 * this piece.</p>
	 * @param pieceType the type of the piece that is introduced
	 * @param anchorPiece the index (starting with 0) of the piece used as anchor
	 * @param hexDirection the relative position of the new piece to the anchor piece
	 */
	@Override
	public void introduce(PieceType pieceType, int anchorPiece,	HexDirection hexDirection) {
		try {
			board.introduce(pieceType, anchorPiece, hexDirection);
			IntroductionNotification note = new IntroductionNotification(board.getIntroduced()-1, hexDirection, anchorPiece, getCurrentPlayer());
			board.setCurrentPlayer(getCurrentPlayer().toggle());
			notifyObservers(note);
		} catch (IllegalStateException e) {
			System.out.println(e.getMessage());
		} catch (IllegalArgumentException e) {
			System.out.println(e.getMessage());
		}

	}

	/**
	 * Decides whether the game is over, or not.
	 * @return {@code true} if the game is over, {@code false} otherwise
	 */
	@Override
	public boolean isGameOver() {
		return board.isGameOver();

	}

	/**
	 * <p>Moves a piece. As for introduction the target position is specified
	 * relatively to an arbitrary piece (which must be a neighbor of the new
	 * position).</p>
	 * <p>Both pieces are specified by its index, starting with 0.</p>
	 * @param piece the index of the piece that is moved
	 * @param anchorPiece the index of the anchor piece
	 * @param hexDirection the direction relatively to the anchor piece
	 */
	@Override
	public void move(int piece, int anchorPiece, HexDirection hexDirection) {
		try{
			int below = board.move(piece, anchorPiece, hexDirection);
			MoveNotification move = new MoveNotification(piece, hexDirection, anchorPiece, below);
			notifyObservers(move);
		}catch(IllegalStateException e){
			e.printStackTrace();
		}catch(IllegalArgumentException e){
			e.printStackTrace();
		}
		
	}

}
